package com.gitee.wsl.coroutines.atomic


import com.gitee.wsl.ext.base.currentTime
import com.gitee.wsl.time.fromMinutes
import com.gitee.wsl.time.fromNanoseconds
import com.gitee.wsl.time.fromSeconds
import kotlinx.atomicfu.locks.ReentrantLock
import kotlinx.atomicfu.locks.reentrantLock
import kotlin.time.Instant
import kotlin.concurrent.Volatile
import kotlin.time.Duration
import kotlin.time.ExperimentalTime


@OptIn(ExperimentalTime::class)
class RateLimiter private constructor(
    private val perDuration: Duration,
    @Volatile
    private var lastAllowed: Instant
) {
    private val lock: ReentrantLock = reentrantLock()


    val isRateLimited: Boolean
        get() = !isAllowed

    val isAllowed: Boolean
        get() {
            if (lock.tryLock()) {
                try {
                    if (lastAllowed.plus(perDuration) > currentTime) {
                        lastAllowed = currentTime
                        return true
                    }
                    return false
                } finally {
                    lock.unlock()
                }
            } else {
                return false
            }
        }

    class Builder {
        private var amount = 0

        fun at1Request(): Builder {
            return atRequests(1)
        }

        fun at2Requests(): Builder {
            return atRequests(2)
        }

        fun at5Requests(): Builder {
            return atRequests(5)
        }

        fun at10Requests(): Builder {
            return atRequests(10)
        }

        fun atRequests(amount: Int): Builder {
            this.amount = amount
            return this
        }

        /*fun per(time: Int, unit: ChronoUnit): RateLimiter {
            return per(Duration.of(time, unit))
        }*/

        fun per(duration: Duration): RateLimiter {
            val perDuration: Duration = duration / amount
            val lastAllowed: Instant = currentTime - duration
            return RateLimiter(perDuration, lastAllowed)
        }

        fun withoutLimits(): RateLimiter {
            return RateLimiter(Duration.fromNanoseconds(1), currentTime)
        }
    }

    companion object {
        val SECOND: Duration = Duration.fromSeconds(1)
        val MINUTE: Duration = Duration.fromMinutes(1)

        fun rateLimit(): Builder {
            return Builder()
        }
    }
}
